// 《围棋》作者版权所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package runtime

// 指标实现导出到运行时/指标。

import (
	"runtime/internal/atomic"
	"unsafe"
)

var (
	// metrics是运行时/度量键到
	// 数据的映射，运行时使用这些数据对每个度量的
	// 值进行采样。
	metricsSema uint32 = 1
	metricsInit bool
	metrics     map[string]metricData

	sizeClassBuckets []float64
	timeHistBuckets  []float64
)

type metricData struct {
	// deps是此度量
	// 所依赖的运行时统计信息集。在调用compute之前，将要传递的statAggregate 
	// 必须确保（）这些依赖关系。
	deps statDepSet

	// compute是一个函数，在给定已填充的statAggregate结构的情况下填充度量值
	// 。
	compute func(in *statAggregate, out *metricValue)
}

// initMetrics初始化度量映射（如果尚未初始化）。
// 
// 必须持有MetricSema。
func initMetrics() {
	if metricsInit {
		return
	}

	sizeClassBuckets = make([]float64, _NumSizeClasses, _NumSizeClasses+1)
	// 跳过大小类0，它是大型对象的替代，但大型
	// 对象是单独跟踪的（它们实际上被放在
	// 最后一个存储桶中，而不是第一个存储桶中）。
	sizeClassBuckets[0] = 1 // 最小分配大小为1字节。
	for i := 1; i < _NumSizeClasses; i++ {
		// 大小类有一个包含的上界
		// 和独占的下界（例如，48字节大小类是
		// （32，48）），而我们需要包含的下界
		// 和独占的上界（例如，48字节大小类是
		// [33，49]）。我们可以通过将所有桶
		// 边界上移1来实现这一点。
		// 
		// 此外，浮点64可以精确表示值高达2^53的整数，大小类相对较小
		// （甚至不接近2^48）因此，这将给我们提供精确的边界。切断第一个索引，表面上是针对大小类0，但大对象是单独跟踪的，所以它实际上是未使用的。切断第一个索引，表面上是针对大小类0，但实际上是大对象对象是单独跟踪的，因此它实际上是未使用的。最底层的bucket包含负值，作为底流单独跟踪，因此手动填写，然后迭代其余部分。statDep依赖于一组统计数据
		sizeClassBuckets[i] = float64(class_to_size[i] + 1)
	}
	sizeClassBuckets = append(sizeClassBuckets, float64Inf())

	timeHistBuckets = timeHistogramMetricsBuckets()
	metrics = map[string]metricData{
		"/gc/cycles/automatic:gc-cycles": {
			deps: makeStatDepSet(sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.sysStats.gcCyclesDone - in.sysStats.gcCyclesForced
			},
		},
		"/gc/cycles/forced:gc-cycles": {
			deps: makeStatDepSet(sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.sysStats.gcCyclesForced
			},
		},
		"/gc/cycles/total:gc-cycles": {
			deps: makeStatDepSet(sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.sysStats.gcCyclesDone
			},
		},
		"/gc/heap/allocs-by-size:bytes": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				hist := out.float64HistOrInit(sizeClassBuckets)
				hist.counts[len(hist.counts)-1] = uint64(in.heapStats.largeAllocCount)
				for i, count := range in.heapStats.smallAllocCount[1:] {
					hist.counts[i] = uint64(count)
				}
			},
		},
		"/gc/heap/allocs:bytes": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.heapStats.totalAllocated
			},
		},
		"/gc/heap/allocs:objects": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.heapStats.totalAllocs
			},
		},
		"/gc/heap/frees-by-size:bytes": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				hist := out.float64HistOrInit(sizeClassBuckets)
				hist.counts[len(hist.counts)-1] = uint64(in.heapStats.largeFreeCount)
				for i, count := range in.heapStats.smallFreeCount[1:] {
					hist.counts[i] = uint64(count)
				}
			},
		},
		"/gc/heap/frees:bytes": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.heapStats.totalFreed
			},
		},
		"/gc/heap/frees:objects": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.heapStats.totalFrees
			},
		},
		"/gc/heap/goal:bytes": {
			deps: makeStatDepSet(sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.sysStats.heapGoal
			},
		},
		"/gc/heap/objects:objects": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.heapStats.numObjects
			},
		},
		"/gc/heap/tiny/allocs:objects": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = uint64(in.heapStats.tinyAllocCount)
			},
		},
		"/gc/pauses:seconds": {
			compute: func(_ *statAggregate, out *metricValue) {
				hist := out.float64HistOrInit(timeHistBuckets)
				hist.counts[0] = atomic.Load64(&memstats.gcPauseDist.underflow)
				for i := range memstats.gcPauseDist.counts {
					hist.counts[i+1] = atomic.Load64(&memstats.gcPauseDist.counts[i])
				}
			},
		},
		"/memory/classes/heap/free:bytes": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = uint64(in.heapStats.committed - in.heapStats.inHeap -
					in.heapStats.inStacks - in.heapStats.inWorkBufs -
					in.heapStats.inPtrScalarBits)
			},
		},
		"/memory/classes/heap/objects:bytes": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.heapStats.inObjects
			},
		},
		"/memory/classes/heap/released:bytes": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = uint64(in.heapStats.released)
			},
		},
		"/memory/classes/heap/stacks:bytes": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = uint64(in.heapStats.inStacks)
			},
		},
		"/memory/classes/heap/unused:bytes": {
			deps: makeStatDepSet(heapStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = uint64(in.heapStats.inHeap) - in.heapStats.inObjects
			},
		},
		"/memory/classes/metadata/mcache/free:bytes": {
			deps: makeStatDepSet(sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.sysStats.mCacheSys - in.sysStats.mCacheInUse
			},
		},
		"/memory/classes/metadata/mcache/inuse:bytes": {
			deps: makeStatDepSet(sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.sysStats.mCacheInUse
			},
		},
		"/memory/classes/metadata/mspan/free:bytes": {
			deps: makeStatDepSet(sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.sysStats.mSpanSys - in.sysStats.mSpanInUse
			},
		},
		"/memory/classes/metadata/mspan/inuse:bytes": {
			deps: makeStatDepSet(sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.sysStats.mSpanInUse
			},
		},
		"/memory/classes/metadata/other:bytes": {
			deps: makeStatDepSet(heapStatsDep, sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = uint64(in.heapStats.inWorkBufs+in.heapStats.inPtrScalarBits) + in.sysStats.gcMiscSys
			},
		},
		"/memory/classes/os-stacks:bytes": {
			deps: makeStatDepSet(sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.sysStats.stacksSys
			},
		},
		"/memory/classes/other:bytes": {
			deps: makeStatDepSet(sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.sysStats.otherSys
			},
		},
		"/memory/classes/profiling/buckets:bytes": {
			deps: makeStatDepSet(sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = in.sysStats.buckHashSys
			},
		},
		"/memory/classes/total:bytes": {
			deps: makeStatDepSet(heapStatsDep, sysStatsDep),
			compute: func(in *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = uint64(in.heapStats.committed+in.heapStats.released) +
					in.sysStats.stacksSys + in.sysStats.mSpanSys +
					in.sysStats.mCacheSys + in.sysStats.buckHashSys +
					in.sysStats.gcMiscSys + in.sysStats.otherSys
			},
		},
		"/sched/goroutines:goroutines": {
			compute: func(_ *statAggregate, out *metricValue) {
				out.kind = metricKindUint64
				out.scalar = uint64(gcount())
			},
		},
		"/sched/latencies:seconds": {
			compute: func(_ *statAggregate, out *metricValue) {
				hist := out.float64HistOrInit(timeHistBuckets)
				hist.counts[0] = atomic.Load64(&sched.timeToRun.underflow)
				for i := range sched.timeToRun.counts {
					hist.counts[i+1] = atomic.Load64(&sched.timeToRun.counts[i])
				}
			},
		},
	}
	metricsInit = true
}

type statDep uint

const (
	heapStatsDep statDep = iota // 对应于heapStatsAggregate 
	sysStatsDep                 // 对应于sysStatsAggregate 
	numStatsDeps
)

// statDepSet表示一组STATDEP。
// 
// 在引擎盖下，它是一个位图。
type statDepSet [1]uint64

// makeStatDepSet从lis创建一个新的statDepSett of statDeps.
func makeStatDepSet(deps ...statDep) statDepSet {
	var s statDepSet
	for _, d := range deps {
		s[d/64] |= 1 << (d % 64)
	}
	return s
}

// difference将s与b的集差作为新集返回。
func (s statDepSet) difference(b statDepSet) statDepSet {
	var c statDepSet
	for i := range s {
		c[i] = s[i] &^ b[i]
	}
	return c
}

// union将两个集的并集作为新集返回。
func (s statDepSet) union(b statDepSet) statDepSet {
	var c statDepSet
	for i := range s {
		c[i] = s[i] | b[i]
	}
	return c
}

// 如果集合中没有依赖项，empty将返回true。如果集合包含给定的statDep，
func (s *statDepSet) empty() bool {
	for _, c := range s {
		if c != 0 {
			return false
		}
	}
	return true
}

// has将返回true。
func (s *statDepSet) has(d statDep) bool {
	return s[d/64]&(1<<(d%64)) != 0
}

// heapSTATSAGREGATE表示从
// 运行时获得的内存统计信息。这组统计信息被分组在一起，因为它们在某种程度上相互依赖，以了解运行时的
// 当前堆内存使用情况。它们也在Ps之间进行切分，因此
// 获取它们是有意义的ll立刻。
type heapStatsAggregate struct {
	heapStatsDelta

	// 从heapStatsDelta中的值派生。

	// inObjects是对象占用的内存字节数，
	inObjects uint64

	// numObjects是堆中活动对象的数量。
	numObjects uint64

	// totalAllocated是在对象的生存期内分配的堆对象的总字节数program.
	totalAllocated uint64

	// totalFreed是在程序生命周期内释放的堆对象的总字节数
	// 。
	totalFreed uint64

	// totalAllocs是在程序生命周期内分配给
	// 的堆对象数。
	totalAllocs uint64

	// totalFrees是在程序生命周期内释放的堆对象数BCDEFG
	totalFrees uint64
}

// compute使用运行时的值填充heapStatsAggregate。
func (a *heapStatsAggregate) compute() {
	memstats.heapStats.read(&a.heapStatsDelta)

	// 计算派生的统计数据。
	a.totalAllocs = uint64(a.largeAllocCount)
	a.totalFrees = uint64(a.largeFreeCount)
	a.totalAllocated = uint64(a.largeAlloc)
	a.totalFreed = uint64(a.largeFree)
	for i := range a.smallAllocCount {
		na := uint64(a.smallAllocCount[i])
		nf := uint64(a.smallFreeCount[i])
		a.totalAllocs += na
		a.totalFrees += nf
		a.totalAllocated += na * uint64(class_to_size[i])
		a.totalFreed += nf * uint64(class_to_size[i])
	}
	a.inObjects = a.totalAllocated - a.totalFreed
	a.numObjects = a.totalAllocs - a.totalFrees
}

// sysStatsAggregate表示从运行时获得的系统内存统计数据
// 因为
// 它们的获取成本相对较低，并且通常相互独立，并且与其他运行时内存统计数据相独立。事实上，它们可能在不同的时间获取，特别是对于
// heapStatsAggregate，这意味着可能存在一些偏差，但由于ABCDEF的原因G
type sysStatsAggregate struct {
	stacksSys      uint64
	mSpanSys       uint64
	mSpanInUse     uint64
	mCacheSys      uint64
	mCacheInUse    uint64
	buckHashSys    uint64
	gcMiscSys      uint64
	otherSys       uint64
	heapGoal       uint64
	gcCyclesDone   uint64
	gcCyclesForced uint64
}

// compute使用运行时的值填充SYSSTATSAGGRAGATE。
func (a *sysStatsAggregate) compute() {
	a.stacksSys = memstats.stacks_sys.load()
	a.buckHashSys = memstats.buckhash_sys.load()
	a.gcMiscSys = memstats.gcMiscSys.load()
	a.otherSys = memstats.other_sys.load()
	a.heapGoal = atomic.Load64(&gcController.heapGoal)
	a.gcCyclesDone = uint64(memstats.numgc)
	a.gcCyclesForced = uint64(memstats.numforcedgc)

	systemstack(func() {
		lock(&mheap_.lock)
		a.mSpanSys = memstats.mspan_sys.load()
		a.mSpanInUse = uint64(mheap_.spanalloc.inuse)
		a.mCacheSys = memstats.mcache_sys.load()
		a.mCacheInUse = uint64(mheap_.cachealloc.inuse)
		unlock(&mheap_.lock)
	})
}

// statAggregate是度量实现的主要驱动因素。
// 
// 它包含运行时统计信息的多个聚合，以及
// 作为已填充的这些聚合的集合。aggergates 
// 由其SURE方法惰性填充。
type statAggregate struct {
	ensured   statDepSet
	heapStats heapStatsAggregate
	sysStats  sysStatsAggregate
}

// 确保填充由deps确定的统计汇总，前提是它们
// 尚未填充。
func (a *statAggregate) ensure(deps *statDepSet) {
	missing := deps.difference(a.ensured)
	if missing.empty() {
		return
	}
	for i := statDep(0); i < numStatsDeps; i++ {
		if !missing.has(i) {
			continue
		}
		switch i {
		case heapStatsDep:
			a.heapStats.compute()
		case sysStatsDep:
			a.sysStats.compute()
		}
	}
	a.ensured = a.ensured.union(missing)
}

// metricValidKind是runtime/metrics.ValueKind的运行时副本，
// 在结构上必须与该类型保持一致。
type metricKind int

const (
	// 这些值必须与运行时/度量包中相应的种类*值
	// 保持一致。
	metricKindBad metricKind = iota
	metricKindUint64
	metricKindFloat64
	metricKindFloat64Histogram
)

// metricSample是runtime/metrics.Sample的运行时副本，
// 必须在结构上与该类型保持相同。
type metricSample struct {
	name  string
	value metricValue
}

// metricValue是runtime/metrics.Sample的运行时副本，
// 必须在结构上与该类型保持相同。
type metricValue struct {
	kind    metricKind
	scalar  uint64         // 包含标量类型的标量值。
	pointer unsafe.Pointer // 包含非标量值。
}

// float64history尝试从值中提取现有的float64Histogram 
// 但如果不存在，则它将分配一个带有给定bucket的float64Histogram 
// 的float64history。
func (v *metricValue) float64HistOrInit(buckets []float64) *metricFloat64Histogram {
	var hist *metricFloat64Histogram
	if v.kind == metricKindFloat64Histogram && v.pointer != nil {
		hist = (*metricFloat64Histogram)(v.pointer)
	} else {
		v.kind = metricKindFloat64Histogram
		hist = new(metricFloat64Histogram)
		v.pointer = unsafe.Pointer(hist)
	}
	hist.buckets = buckets
	if len(hist.counts) != len(hist.buckets)-1 {
		hist.counts = make([]uint64, len(buckets)-1)
	}
	return hist
}

// MetricFloat64直方图是runtime/metrics.Float64直方图的运行时副本
// 必须在结构上保持与该类型相同。
type metricFloat64Histogram struct {
	counts  []uint64
	buckets []float64
}

// agg由readMetrics使用，并受metricsSema保护。
// 
// 作为全局变量管理，因为它的指针将是
// 一个动态定义函数的参数，我们希望
// 避免它逃逸到堆中。
var agg statAggregate

// readMetrics是runtime/metrics.Read的实现。
// 
// go:linkname readMetrics runtime/metrics.runtime\u readMetrics 
func readMetrics(samplesp unsafe.Pointer, len int, cap int) {
	// 从参数构造一个切片。
	sl := slice{samplesp, len, cap}
	samples := *(*[]metricSample)(unsafe.Pointer(&sl))

	// 获取MetricSema，但需要切换。此操作
	// 的开销足够大，因此排队等待goroutine并在它们之间关闭
	// 将明显表现得更好。
	semacquire1(&metricsSema, true, 0, 0)

	// 确保映射已初始化。
	initMetrics()

	// 以防御方式清除agg。
	agg = statAggregate{}

	// 样本。
	for i := range samples {
		sample := &samples[i]
		data, ok := metrics[sample.name]
		if !ok {
			sample.value.kind = metricKindBad
			continue
		}
		// 确保我们拥有所需的所有数据。
		// agg是惰性填充的。
		agg.ensure(&data.deps)

		// 根据我们的统计数据计算值。
		data.compute(&agg, &sample.value)
	}

	semrelease(&metricsSema)
}
